/*
 * Copyright 2013 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package exp.algorithm.sic;

import java.util.List;

import com.alibaba.simpleimage.analyze.sift.FloatArray;

import exp.algorithm.sic.feature.DensePeakFinder;
import exp.algorithm.sic.feature.ExtremePeakFinder;
import exp.algorithm.sic.feature.FeatureVector;
import exp.algorithm.sic.feature.FeatureVectorMaker;
import exp.algorithm.sic.feature.OriginalFeatureVectorMaker;
import exp.algorithm.sic.feature.PeakFinder;
import exp.algorithm.sic.feature.ShapeFeatureMaker;
import exp.algorithm.sic.feature.ShapeStatisticsFeatureMaker;
import exp.algorithm.sic.feature.StatisticsFeatureMaker;

/**
 * 
 * 
 * @author Alex
 * 
 * createFeatureDescriptor这个方法重写,原来是传入的this.diffImgs
 * 现在修改,改成this.smoothImgs 
 * 
 * 
 * 类Octave.java的实现描述：表示8度金字塔中的一个8度空间，即以尺寸为坐标的某一尺寸上的那个8度空间
 * 
 * @author axman 2013-6-27 上午11:30:08
 */
public class OctaveSpace {

	
	//多个点为一个segment
	public final static int M = 5;
	//找几个点
	public final static int N = 5;
	
	//如何根据特征点形成特征向量

	//如何查找特征点 ScalePeak
	PeakFinder pk;
	FeatureVectorMaker  fvm ;
	
	
	OctaveSpace down; // down指的是下一个8度空间
	OctaveSpace up;
	TimeSeries baseImg; // 当前8度空间的原始图片，由上一个8度空间的某层（默认为倒数第三层）获取
	public float baseScale; // 原始图片在塔中的原始尺度
	public int indexInPyramid;
	public TimeSeries[] smoothedImgs; // 同一尺寸用不同模糊因子模糊后的高斯图像集
	public TimeSeries[] diffImags; // 由smoothedImgs得到的差分图集

//	private TimeSeries[] magnitudes;
//	private TimeSeries[] directions;

	public OctaveSpace(PeakFinderType pft,VectorMakerType vmt) {
		switch(pft){
		case EXTREME:
			pk = new ExtremePeakFinder();
			break;
		case DENSE:
			pk = new DensePeakFinder();
			break;
		default:
			pk = new ExtremePeakFinder();
		}
		switch (vmt) {
		case SHAPE:
			fvm = new ShapeFeatureMaker();
			break;
		case STASTICS:
			fvm = new StatisticsFeatureMaker();
			break;
		case STATSHAPE:
			fvm = new ShapeStatisticsFeatureMaker();
			break;
		case ORIGINAL:
			fvm = new OriginalFeatureVectorMaker();
			break;
		default:
			fvm = new StatisticsFeatureMaker();
			break;
		}
		
		
	}
	
	
	public String toString(){
		String s ="FeatureMakerClass:%s,Pyramid ID:%d,Smoothed Length:%d,Diff Length:%d";
		return String.format(s, fvm.getClass().getName(),indexInPyramid,smoothedImgs.length,diffImags.length);
	}
	
	/**
	 * @return 返回下一8度空间的原始基准图象
	 * @see page5 of
	 *      "Distinctive Image Features from Scale-Invariant featurePoints"
	 *      (David G.Lowe @January 5, 2004)
	 */
	// 高斯函数G对图像I的模糊函数 L(x,y,σ) = G(x,y,σ) * I(x,y)
	// 高斯差分函数:D(x,y,σ) = (G(x,y,kσ)−G(x,y,σ)) * I(x,y) = L(x,y,kσ)
	// L(x,y,σ) 对于scales幅图象产生连续尺度，推导 k = 2 ^ (1/s)，论文中默认
	// scales为3所以一共6幅图像，它们的尺度应该为
	// 1σ,1.26σ,1.59σ,2.0σ,2.52σ,3.17σ
	// 倒数第三幅正好发生一个二倍的阶跃，把它作为下一个8度空间的第一幅图片，保证差分金字塔的尺度空间的连续性，其实对于任义scales,length-2为固定的位置，
	// 因为smoothedImgs长度为s+3,前面去掉1个原始图片，只有length-2的时 k = 2 ^ (s/s)才正好是一个2倍的阶跃
	public TimeSeries getLastGaussianImg() {
		if (this.smoothedImgs.length < 2) {
			throw new java.lang.IllegalArgumentException(
					"err: too few gaussian maps.");
		}
		return (this.smoothedImgs[this.smoothedImgs.length - 2]);
	}

	/**
	 * 在一个8空间用不同的模糊因子构造更多层的高期模糊图像集,这里是不同模糊因子的模糊但是尺寸是相同的
	 * 
	 * @param first
	 * @param firstScale
	 * @param scales
	 * @param sigma
	 */
	public void makeGaussianImgs(TimeSeries base, float baseScale,int scales, float sigma) {
		// 对于DOG(差分图像集)我们需要一张以上的图片才能生成差分图，但是查找极值点更多要差分图。见buildDiffMaps
		smoothedImgs = new TimeSeries[scales + 3];
		// 每一个极值点是在三维空间中比较获得，即要和它周围8个点和上一幅对应的9个点以及下一幅对应的9个点，因此为了获得scales层点，
		// 那么在差分高斯金字塔中需要有scales+2幅图像,而如果差分图幅数是scales+2，那么8度空间中至少需要scales+2＋1幅高斯模糊图像。
		this.baseScale = baseScale;
		TimeSeries prev = base;
		smoothedImgs[0] = base;

		float w = sigma;
		float kTerm = (float) Math.sqrt(Math.pow(Math.pow(2.0, 1.0 / scales),2.0) - 1.0);
		for (int i = 1; i < smoothedImgs.length; i++) {
			GaussianArray gauss = new GaussianArray(w * kTerm);
			prev = smoothedImgs[i] = gauss.convolve(prev);
			w *= Math.pow(2.0, 1.0 / scales);
		}
	}

	public void makeGaussianDiffImgs() {
		diffImags = new TimeSeries[smoothedImgs.length - 1];
		for (int sn = 0; sn < diffImags.length; sn++) {
			diffImags[sn] = TimeSeries.minus(smoothedImgs[sn + 1],smoothedImgs[sn]);
		}
	}
	//TODO : Entrance
	//使用Extreme找特征点使用diffImgs,计算特征向量使用diffImgs
	public List<ScalePeak> findPeaks(float dogThresh) {
		return pk.find(dogThresh, this.diffImags);
	}
	public  List<FeatureVector> createFeatureDescriptor(List<ScalePeak> peaks){
		return fvm.createFeatureDescriptor(peaks, this.smoothedImgs);
    }
	public void clear() {
//		for (int i = 0; i < this.magnitudes.length; i++)
//			this.magnitudes[i] = null;
//		for (int i = 0; i < this.directions.length; i++)
//			this.directions[i] = null;
//		magnitudes = directions = null;
	}

	/**
	 * 用于传递引用的数据结构
	 */
	static class RefCheckMark {

		boolean isMin;
		boolean isMax;
	}

	/**
	 * 用于传递引用的数据结构
	 */
	static class RefPeakValueAndDegreeCorrection {

		float peakValue;
		float degreeCorrection;
	}

}
